import json
import os
import random
import time
from urllib import request

import cv2
import pyautogui
import requests
from selenium import webdriver


def create_chrome_driver(*, headless=False, images=False):
    """
    创建浏览器驱动
    :param headless: 浏览器隐藏配置参数 (True/False)
    :param images: 无图加载配置参数 (True/False)
    :return: 浏览器驱动节点
    """
    print("正在创建浏览器驱动配置...")
    options = webdriver.ChromeOptions()
    if headless:
        print("正在配置隐藏模式...")
        options.add_argument('--headless')

    if images:
        print("正在配置无图加载模式...")
        preferences = {
            'profile.default_content_setting_values': {
                'images': 2,
            }
        }
        options.add_experimental_option('prefs', preferences)

    print("正在配置伪装浏览器参数...")
    options.add_experimental_option('excludeSwitches', ['enable-automation'])
    options.add_experimental_option('useAutomationExtension', False)

    print("正在创建浏览器驱动...")
    browser = webdriver.Chrome(options=options)

    # webdriver: True => undefined
    print("正在添加反爬措施...")
    browser.execute_cdp_cmd(
        'Page.addScriptToEvaluateOnNewDocument',
        {'source': 'Object.defineProperty(navigator, "webdriver", {get: () => undefined})'}
    )
    return browser


def login(browser, info):
    """
    模拟登录框架自定义函数
    :param browser: 浏览器驱动节点
    :param info: 登录信息字典对象
    :return: 浏览器驱动节点
    """
    send_keys_interval(browser.find_element_by_id(info['username_id']), info['username'])
    send_keys_interval(browser.find_element_by_id(info['password_id']), info['password'])
    time.sleep(random.random())
    browser.find_element_by_id(info['submit_id']).click()
    time.sleep(random.randint(1, 3))
    browser = drag_block(browser, info['retry_times'], info['position'])
    return browser


def get_cookies(browser, cookies_name):
    """
    获取登录的 cookie 数据并且保存文件
    :param browser: 浏览器驱动节点
    :param cookies_name: cookie保存的文件名
    """
    with open(cookies_name, 'w') as file:
        json.dump(browser.get_cookies(), file)


def add_cookies(browser, cookie_file):
    """
    添加 cookies 到浏览器驱动配置文件
    :param browser: 浏览器驱动节点
    :param cookie_file: cookie文件路径名
    """
    # 向浏览器写入 cookie 信息
    with open(cookie_file, 'r') as file:
        cookie_list = json.load(file)
        for cookie_dict in cookie_list:
            if cookie_dict['secure']:
                browser.add_cookie(cookie_dict)


def send_keys_interval(element, text, interval=0.1):
    """
    模拟人工输入文本信息
    :param element: 账号密码元素
    :param text: 账号密码值
    :param interval: 输入时延 (秒)
    """
    for c in text:
        element.send_keys(c)
        time.sleep(random.randint(int(interval * 500), int(interval * 1500)) / 1000)


def find_pic(background, slide):
    """
    获取背景图与滑块图的最佳位置
    :param background: 背景图路径
    :param slide: 滑块图路径
    :return: 偏移量
    """
    # 读取背景灰度图片
    background_gray = cv2.imread(background, 0)
    # 读取滑块灰度图片
    slide_gray = cv2.imread(slide, 0)
    # 匹配滑块位置
    res = cv2.matchTemplate(background_gray, slide_gray, cv2.TM_CCOEFF_NORMED)
    # 获取最佳与最差匹配
    value = cv2.minMaxLoc(res)
    return value[2][0]


def slide_by_autogui(x, y, offset):
    """
    实现滑块并自定义轨迹方程
    :param x: x坐标值
    :param y: y坐标值
    :param offset: 偏移量
    """
    xo = x + offset
    pyautogui.moveTo(x, y, duration=0.1)
    pyautogui.mouseDown()
    y += random.randint(9, 19)
    pyautogui.moveTo(x + int(offset * random.randint(15, 23) / 20), y, duration=0.28)
    y += random.randint(-9, 0)
    pyautogui.moveTo(x + int(offset * random.randint(17, 21) / 20), y, duration=random.randint(20, 31) / 100)
    y += random.randint(0, 8)
    pyautogui.moveTo(xo, y, duration=0.3)
    pyautogui.mouseUp()


def drag_block(browser, retry_times, position):
    """
    拖动滑块
    :param browser: 浏览器驱动节点
    :param retry_times: 重复尝试次数
    :param position: 坐标字典对象
    :return: 浏览器驱动节点
    """
    for i in range(retry_times):
        try:
            background = browser.find_element_by_xpath(r'//div/div[@class="JDJRV-bigimg"]/img')
        except:
            print("============登录成功============")
            break
        slide = browser.find_element_by_xpath(r'//div/div[@class="JDJRV-smallimg"]/img')
        background_url = background.get_attribute("src")
        slide_url = slide.get_attribute("src")
        background_img = './verification/background.png'
        slide_img = './verification/slide.png'
        request.urlretrieve(background_url, background_img)
        print("=======滑块背景图获取成功=======")
        request.urlretrieve(slide_url, slide_img)
        print("=======验证滑块图获取成功=======")
        x = find_pic(background_img, slide_img)
        print('本地最佳偏移量: %d' % x)
        # 获取下载背景图宽度
        w1 = cv2.imread(background_img).shape[1]
        # 获取网页背景图宽度
        w2 = background.size['width']
        # 计算实际页面 x 偏移量
        x = x * w2 / w1
        print('实际最佳偏移量: %d' % x)
        # x：屏幕左上角至滑块中心的横向像素值
        # y：屏幕左上角至滑块中心的纵向像素值
        slide_by_autogui(position['x'], position['y'], x * position['b'])
    return browser


def pull_down(browser, headless=False, sleep=0.5):
    """
    下拉滚动条
    :param browser: 浏览器驱动节点
    :param headless: 浏览器隐藏配置参数 (True/False)
    :param sleep: 下拉滚动时延设置
    """
    span = int(browser.execute_script('return document.body.scrollHeight/document.documentElement.clientHeight;'))
    if headless:
        span += 6  # 自适应调节图片之间的间距
    for i in range(span):
        browser.execute_script(
            'window.scrollTo(0, document.documentElement.clientHeight*%d)' % i)
        time.sleep(sleep)


def download_pic(html, goods_name, pic_index, page_count):
    """
    下载图片
    :param html: 解析页面文件
    :param goods_name: 商品名称
    :param pic_index: 起始页图片索引
    :param page_count: 每页商品列表的最大数量
    """
    print("正在创建图片索引: %d~%d" % (pic_index, pic_index + page_count))
    count = 1
    for num in range(pic_index, pic_index + page_count):
        img = html.xpath('//*[@id="J_goodsList"]/ul/li[{}]/div/div[1]/a/img/@src'.format(count))
        count += 1
        for item in img:
            if not os.path.exists('./download/%s' % goods_name):
                print("正在创建 【%s】 文件夹..." % goods_name)
                os.makedirs('./download/%s' % goods_name)
                print("【%s】 文件夹创建完毕..." % goods_name)
            img_url = "https:" + item
            img_url = img_url.strip('.avif')
            if item is None:
                print("第 %d 张%s图片获取失败: %s" % (num, goods_name, img_url))
            else:
                headers = {
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
                                  "Chrome/84.0.4147.125 Safari/537.36"}
                data = requests.get(img_url, headers=headers)
                print("正在下载第 %d 张 【%s】 图片: %s" % (num, goods_name, img_url))
                try:
                    with open('./download/%s/%d.jpg' % (goods_name, num), 'wb') as f:
                        f.write(data.content)
                except Exception as e:
                    print("下载失败：第 %d 张 【%s】 图片: %s" % (num, goods_name, img_url))
                    print(e)
